using System;
using System.Collections.Generic;
using System.Text;
using FMOD;
using LeisureCenter.Api;

namespace FModPlayer
{
    public abstract class BaseFModMedia : BasePositionableMedia, IDisposable
    {
        protected FModPlayer player;
        protected FMOD.Sound sound;
        protected FMOD.Channel channel;
        protected CHANNEL_CALLBACK callback;
        protected int duration;

        protected BaseFModMedia(FModPlayer player, string fileName)
            : base(fileName)
        {
            this.player = player;
            // callback has to be referenced here because it is destined for unmanaged code
            this.callback = new CHANNEL_CALLBACK(ChannelCallback);
        }

        protected bool disposed = false;

        ~BaseFModMedia()
        {
            Dispose(false);
        }

        protected void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                DoStop();
                this.disposed = true;
            }
        }

        protected bool CheckResult(FMOD.RESULT result)
        {
            switch (result)
            {
                case RESULT.OK:
                    LoadingState = MediaLoadingState.Loaded;
                    loadingErrorMessage = "";
                    return true;
                case RESULT.ERR_FILE_NOTFOUND:
                    LoadingState = MediaLoadingState.NotFound;
                    loadingErrorMessage = FMOD.Error.String(result);
                    break;
                default:
                    LoadingState = MediaLoadingState.Error;
                    loadingErrorMessage = FMOD.Error.String(result);
                    break;
            }
            return false;
        }

        protected abstract void CreateSoundIfNeeded();

        protected virtual void CreateChannelIfNeeded(bool paused)
        {
            if (channel != null)
            {
                channel.setCallback(CHANNEL_CALLBACKTYPE.END, this.callback, 0);
            }
        }

        protected RESULT ChannelCallback(IntPtr channelraw, CHANNEL_CALLBACKTYPE type, int command, uint commanddata1, uint commanddata2)
        {
            if (type == CHANNEL_CALLBACKTYPE.END)
            {
                player.updateTimer.Stop();
                OnMediaEnded();
            }
            return RESULT.OK;
        }

        #region IPositionableMedia implementation

        // all times in seconds
        public override int Duration
        {
            get
            {
                if (this.duration == 0)
                {
                    CreateSoundIfNeeded();
                    if (sound != null)
                    {
                        uint length = 0;
                        CheckResult(sound.getLength(ref length, TIMEUNIT.MS));
                        this.duration = (int)length / 1000;
                    }
                }
                return this.duration;
            }
        }

        public override int Position
        {
            get
            {
                CreateSoundIfNeeded();
                if (channel != null)
                {
                    uint position = 0;
                    CheckResult(channel.getPosition(ref position, TIMEUNIT.MS));
                    return (int)position / 1000;
                }
                return 0;
            }

            set
            {
                CreateSoundIfNeeded();
                if (channel != null)
                {
                    CheckResult(channel.setPosition((uint)value * 1000, TIMEUNIT.MS));
                }
            }
        }

        // playback
        protected override void DoPlay()
        {
            CreateSoundIfNeeded();
            if (channel != null)
            {
                channel.setPaused(false);
            }
            else
            {
                CreateChannelIfNeeded(false);
            }
            if (channel != null)
            {
                PlayState = MediaPlayState.Playing;
                player.updateTimer.Start();
            }
        }

        protected override void DoPause()
        {
            CreateSoundIfNeeded();
            if (channel != null)
            {
                CheckResult(channel.setPaused(true));
                PlayState = MediaPlayState.Paused;
            }
        }

        protected override void DoStop()
        {
            if (channel != null)
            {
                channel.setCallback(CHANNEL_CALLBACKTYPE.END, null, 0);
                CheckResult(channel.stop());
                channel = null;
            }
            if (sound != null)
            {
                sound.release();
                sound = null;
            }
            PlayState = MediaPlayState.Stopped;
        }

        #endregion

        #region IDisposable Membres

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}